﻿//++
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
// Module Name:
//
//  JoinNowInstantMessageService.cs
//    
// Abstract:
//
// Workflow service class for performing the asynchronous operations for Join Now
// via Instant Message and then notifying the appropriate activities when the operation
// is complete.
//  
//--

using System;
using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.OfficeCommunicationsServer.Applications.Common;
using Microsoft.Rtc.Collaboration;
using Microsoft.Rtc.Signaling;
using System.Text;

namespace Microsoft.EnhancedReminders.Dispatcher.Workflow
{
    /// <summary>
    /// Workflow service class for performing the asynchronous operations for Join Now
    /// via Instant Message and then notifying the appropriate activities when the operation
    /// is complete.
    /// </summary>
    public class JoinNowInstantMessageService : JoinNowAbstractService
	{
        /// <summary>
        /// Name of SIP header to specify the conversation ID for joining the conference.
        /// </summary>
        private const string ConversationIdHeaderName = "ms-conversation-id";

        /// <summary>
        /// ctor.
        /// </summary>
        public JoinNowInstantMessageService() : base()
        {
        }

        /// <summary>
        /// Begins the asynchronous operations necessary to transfer the call to a conference.
        /// </summary>
        protected override void JoinConference(Guid jobId, Call erCall, CalendarNotificationItem notification)
        {
            Debug.Assert(erCall != null, "erCall");
            Debug.Assert(erCall is InstantMessagingCall, "erCall is InstantMessagingCall");
            Debug.Assert(notification != null, "notification != null");
            Debug.Assert(notification.ReminderItem != null, "notification.ReminderItem != null");
            Debug.Assert(!String.IsNullOrEmpty(notification.ReminderItem.ConferenceAddress), "conferenceAddress");

            // Notify the user that join now is starting...
            InstantMessagingCall imCall = Helpers.AssertCast<InstantMessagingCall>(erCall);
            ConferencingInformation joinNowInfo = new ConferencingInformation(notification.Context, erCall, notification, jobId);
            imCall.Flow.BeginSendMessage(IMResources.JoinNow_Starting, StartJoinNow, joinNowInfo);
        }

        /// <summary>
        /// Join the meeting.
        /// </summary>
        private void StartJoinNow(IAsyncResult asyncResult)
        {
            Debug.Assert(asyncResult != null, "asyncResult != null");

            ConferencingInformation joinNowInfo = Helpers.AssertCast<ConferencingInformation>(asyncResult.AsyncState);
            InstantMessagingCall imCall = Helpers.AssertCast<InstantMessagingCall>(joinNowInfo._call);

            Debug.Assert(imCall != null, "call");
            Debug.Assert(imCall.Flow != null, "imCall.Flow");
            Debug.Assert(imCall.Conversation != null, "imCall.Conversation");
            Debug.Assert(!String.IsNullOrEmpty(imCall.Conversation.Id), "imCall.Conversation.ID");
            Debug.Assert(imCall.Conversation.Endpoint != null, "imCall.Conversation.Endpoint");
            Debug.Assert(imCall.Conversation.Endpoint.InnerEndpoint != null, "imCall.Conversation.Endpoint.InnerEndpoint");
            Debug.Assert(imCall.RemoteEndpoint.Participant != null, "imCall.RemoteEndpoint.Participant");
            Debug.Assert(imCall.RemoteEndpoint.Participant.Uri != null, "imCall.RemoteEndpoint.Participant.Uri");

            try
            {
                imCall.Flow.EndSendMessage(asyncResult);

                RealTimeEndpoint realEndpoint = imCall.Conversation.Endpoint.InnerEndpoint;
                RealTimeAddress target = new RealTimeAddress(imCall.RemoteEndpoint.Participant.Uri);

                SignalingSession conferenceSession = new SignalingSession(realEndpoint, target);
                conferenceSession.OfferAnswerNegotiation = new JoinNowOfferAnswer(joinNowInfo);

                List<SignalingHeader> headers = new List<SignalingHeader>();
                headers.Add(new SignalingHeader(ConversationIdHeaderName, imCall.Conversation.Id));

                Logger.WriteTraceInformation(joinNowInfo._context, "Attempting to join the conference.");

                joinNowInfo._signalingSession = conferenceSession;
                conferenceSession.BeginParticipate(headers, JoinCompleted, joinNowInfo);
            }
            catch (Exception exn)
            {
                Logger.WriteTraceError(joinNowInfo._context, exn, "Failed to join conference due to unexpected error.");
                Helpers.FilterIrrecoverableExceptions(exn);
                NotifyWorkflowOfCompletedEvent(joinNowInfo._jobId);
            }
        }

        /// <summary>
        /// Async callback for when the conference has been joined.
        /// </summary>
        private void JoinCompleted(IAsyncResult asyncResult)
        {
            Debug.Assert(asyncResult != null, "asyncResult != null");

            ConferencingInformation joinNowInfo = Helpers.AssertCast<ConferencingInformation>(asyncResult.AsyncState);

            try
            {
                Debug.Assert(joinNowInfo._signalingSession != null, "joinNowInfo._signalingSession");
                joinNowInfo._signalingSession.EndParticipate(asyncResult);

                Logger.WriteTraceInformation(joinNowInfo._context, "Successfully joined the conference.");

                // Notify the user that join now succeeded and that they can click the Live Meeting icon 
                // (orange icon on the top right corner of Communicator) to open Live Meeting.
                // Notify the user that join now succeeded and that they can click the Live Meeting icon 
                // (orange icon on the top right corner of Communicator) to open Live Meeting.
                InstantMessagingCall imCall = Helpers.AssertCast<InstantMessagingCall>(joinNowInfo._call);

                StringBuilder message = new StringBuilder(IMResources.JoinNow_Succeeded);
                if (joinNowInfo.IsLiveMeeting)
                {
                    message.Append("\r\n");
                    message.Append(IMResources.JoinNow_LiveMeetingHint); // Point out the Live Meeting button.
                }

                imCall.Flow.BeginSendMessage(message.ToString(), SendMessageCompleted, joinNowInfo);
            }
            catch (Exception e)
            {
                Logger.WriteTraceError(joinNowInfo._context, e, "Failed to join conference due to unexpected error.");
                Helpers.FilterIrrecoverableExceptions(e);
                NotifyWorkflowOfCompletedEvent(joinNowInfo._jobId);
            }
        }

        /// <summary>
        /// Async callback for when the join now succeeded message has completed.  The join 
        /// now operation is now completed. 
        /// </summary>
        private void SendMessageCompleted(IAsyncResult asyncResult)
        {
            Debug.Assert(asyncResult != null, "asyncResult != null");

            ConferencingInformation joinNowInfo = Helpers.AssertCast<ConferencingInformation>(asyncResult.AsyncState);

            try
            {
                InstantMessagingCall imCall = Helpers.AssertCast<InstantMessagingCall>(joinNowInfo._call);
                imCall.Flow.EndSendMessage(asyncResult);
            }
            catch (Exception exn)
            {
                Logger.WriteTraceError(joinNowInfo._context, exn, "Failed to join conference due to unexpected error.");
                Helpers.FilterIrrecoverableExceptions(exn);
            }
            finally
            {
                NotifyWorkflowOfCompletedEvent(joinNowInfo._jobId);
            }
        }
	}
}
